package com.igbeok.miscellaneous.concurrency.notify;


/**
 * 这段代码启动了三个简单的wait线程，当他们处于等待状态以后，试图由一个notify线程来唤醒。
 * 转载注明出处：http://x- spirit.javaeye.com/、http: //www.blogjava.net/zhangwei217245/
 * 运行这段程序，你会发现，满屏的java.lang.IllegalMonitorStateException，根本不是你想要的结果。
 * 转载注明出处：http://x- spirit.javaeye.com/、http: //www.blogjava.net/zhangwei217245/
 * 请注意以下几个事实：
 *    1. 任何一个时刻，对象的控制权（monitor）只能被一个线程拥有。
 *    2. 无论是执行对象的wait、notify还是notifyAll方法，必须保证当前运行的线程取得了该对象的控制权（monitor）。
 *    3. 如果在没有控制权的线程里执行对象的以上三种方法，就会报java.lang.IllegalMonitorStateException异常。
 *    4. JVM基于多线程，默认情况下不能保证运行时线程的时序性。
 * 
 * 也就是说，当线程在调用某个对象的wait或者notify方法的时候，要先取得该对象的控制权，换句话说，就是进入这个对象的监视器。
 * 转载注明出处：http://x- spirit.javaeye.com/、http: //www.blogjava.net/zhangwei217245/
 * 通过前面对同步的讨论，我们知道，要让一个线程进入某个对象的监视器，通常有三种方法：
 * 转载注明出处：http://x- spirit.javaeye.com/、http: //www.blogjava.net/zhangwei217245/
 * 1： 执行对象的某个同步实例方法
 * 2： 执行对象对应的同步静态方法
 * 3： 执行对该对象加同步锁的同步块
 * 
 * 但是，运行这个程序，我们发现事与愿违。那个非法监视器异常又出现了。。。
 * 转载注明出处：http://x- spirit.javaeye.com/、http: //www.blogjava.net/zhangwei217245/
 * 我们注意到，针对flag的同步块中，我们实际上已经更改了flag对对象的引用： flag="false";
 * 转载注明出处：http://x- spirit.javaeye.com/、http: //www.blogjava.net/zhangwei217245/
 * 显然，这样一来，同步块也无能为力了，因为我们根本不是针对唯一的一个对象在进行同步。
 * 
 * 我们不妨将flag封装到JavaBean或者数组中去，这样用JavaBean对象或者数组对象进行同步，就可以达到既能修改里面参数又不耽误同步的目的。
 * @author ricoyu
 *
 */
public class NotifyTest2 {
	private String flag = "true";

	class NotifyThread extends Thread {
		public NotifyThread(String name) {
			super(name);
		}

		public void run() {
			try {
				sleep(3000);// 推迟3秒钟通知
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			synchronized (flag) {
				flag = "false";
				flag.notify();
			}
		}
	};

	class WaitThread extends Thread {
		public WaitThread(String name) {
			super(name);
		}

		public void run() {

			while (flag != "false") {
				System.out.println(getName() + " begin waiting!");
				long waitTime = System.currentTimeMillis();
				try {
					synchronized (flag) {
						flag.wait();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				waitTime = System.currentTimeMillis() - waitTime;
				System.out.println("wait time :" + waitTime);
			}
			System.out.println(getName() + " end waiting!");

		}
	}

	public static void main(String[] args) throws InterruptedException {
		System.out.println("Main Thread Run!");
		NotifyTest2 test = new NotifyTest2();
		NotifyThread notifyThread = test.new NotifyThread("notify01");
		WaitThread waitThread01 = test.new WaitThread("waiter01");
		WaitThread waitThread02 = test.new WaitThread("waiter02");
		WaitThread waitThread03 = test.new WaitThread("waiter03");
		notifyThread.start();
		waitThread01.start();
		waitThread02.start();
		waitThread03.start();
	}

}